#include <stdio.h>
#include "cmsis_os2.h"
#include "wifiiot_i2c.h"
#include "oled.h"
#include <unistd.h>
#include "ohos_init.h"
#include "ohos_types.h"
#include "wifiiot_gpio.h"
#include "oled_fonts.h"
#include <math.h>
#include <stdlib.h>
#include <string.h>
#define RESET_TIME (100 * 1000) // 100ms
#define OLED_SPEED 100000 // 100KHz
#define OLED_IDX WIFI_IOT_I2C_IDX_1

static uint8_t oledBuffer[OLED_BUFFER];

/**
 * @brief  发送数据
 * @param  data: 数据
 * @param  size: 数据大小
 */
static void sendData(uint8_t* data ,size_t size){
    WifiIotI2cIdx id =OLED_IDX;
    WifiIotI2cData i2data={0};
    i2data.sendBuf=data;
    i2data.sendLen=size;
    I2cWrite(id,OLED_ADDR,&i2data);
}

/**
 * @brief  写入数据
 * @param  data: 数据
 */
static void oledWriteData(uint8_t data){
    uint8_t buffer[2]={0x40,data};
    sendData(buffer,sizeof(buffer));
}

/**
 * @brief  写入命令
 * @param  cmd: 命令
 */
static void oledWriteCmd(uint8_t cmd){
    uint8_t buffer[2]={0x00,cmd};
    sendData(buffer,sizeof(buffer));
}

/**
 * @brief  初始化oled
 */
void oledInit(void){
    usleep(RESET_TIME);
    I2cInit(OLED_IDX,OLED_SPEED);
    oledWriteCmd(0xAE); // display off
    oledWriteCmd(0x20); // Set Memory Addressing Mode
    oledWriteCmd(0x00); // 00,Horizontal addressing mode (RESET)

    //oledWriteCmd(0xb0); // Set Page Start Address for Page Addressing Mode,0-7
    
    oledWriteCmd(0xc8); // Set COM Output Scan Direction

    //oledWriteCmd(0x00); // ---set low column address
    //oledWriteCmd(0x10); // ---set high column address

    oledWriteCmd(0x21); // Set Column Address
    oledWriteCmd(0x00); // Start Address
    oledWriteCmd(0x7f); // End Address
    oledWriteCmd(0x22); // Set Page Address
    oledWriteCmd(0x00); // Start Page
    oledWriteCmd(0x07); // End Page
    oledWriteCmd(0x40); // --set start line address
    oledWriteCmd(0x81); // --set contrast control register
    oledWriteCmd(0xff); //  亮度调节 0x00~0xff
    oledWriteCmd(0xa1); // --set segment re-map 0 to 127
    oledWriteCmd(0xa6); // --set normal display
    oledWriteCmd(0xa8); // --set multiplex ratio(1 to 64)
    oledWriteCmd(0x3F); //
    oledWriteCmd(0xa4); // 0xa4,Output follows RAM content;0xa5,Output ignores RAM content
    oledWriteCmd(0xd3); // -set display offset
    oledWriteCmd(0x00); // -not offset
    oledWriteCmd(0xd5); // --set display clock divide ratio/oscillator frequency
    oledWriteCmd(0xf0); // --set divide ratio
    oledWriteCmd(0xd9); // --set pre-charge period
    oledWriteCmd(0x22); //
    oledWriteCmd(0xda); // --set com pins hardware configuration
    oledWriteCmd(0x12);
    oledWriteCmd(0xdb); // --set vcomh
    oledWriteCmd(0x20); //  0x20,0.77xVcc
    oledWriteCmd(0x8d); // --set DC-DC enable
    oledWriteCmd(0x14); //
    oledWriteCmd(0xaf); // --turn on oled panel
}

/**
 * @brief  设置位置
 * @param  startX: 起始列
 * @param  startY: 起始页
 * @param  endX: 结束列
 * @param  endY: 结束页
 */
static void setPos(uint8_t startX, uint8_t startY,uint8_t endX, uint8_t endY){
    oledWriteCmd(0x21); // Set Column Address
    oledWriteCmd(0x00+startX); // Start Address
    oledWriteCmd(0x00+endX); // End Address
    oledWriteCmd(0x22); // Set Page Address
    oledWriteCmd(0x00+startY); // Start Page
    oledWriteCmd(0x00+endY); // End Page
}

/**
 * @brief  将显存写入oled
 */
void oledUpdate(void){
    setPos(0,0,127,7);
    uint8_t buffer[OLED_BUFFER+1]={0x40};
    for(int i=0;i<OLED_BUFFER;i++){
        buffer[i+1]=oledBuffer[i];
    }
    sendData(buffer,sizeof(buffer));
}

/**
 * @brief  清空oled及显存
 */
void oledClear(void){
    for(int i=0;i<OLED_BUFFER;i++){
        oledBuffer[i]=0x00;
    }
    //oledUpdate();
}

/**
 * @brief  填充oled
 * @param  data: 数据
 */
void oledFill(uint8_t data){
    for(int i=0;i<OLED_BUFFER;i++){
        oledBuffer[i]=data;
    }
    oledUpdate();
}

/**
 * @brief  写入一个点
 * @param  x: 列
 * @param  y: 行
 * @param  color: 颜色
 */
void writePoint(uint8_t x, uint8_t y, oledColor color){
    if(x>=OLED_WIDTH || y>=OLED_HEIGHT){
        return;
    }
    uint16_t pos=x+y/8*OLED_WIDTH;
    uint8_t mask=1<<(y%8);
    if(color==WHITE){
        oledBuffer[pos]|=mask;
    }else{
        oledBuffer[pos]&=~mask;
    }
}

/**
 * @brief  画线
 * @param  x1: 起始列
 * @param  y1: 起始行
 * @param  x2: 结束列
 * @param  y2: 结束行
 * @param  color: 颜色
 */
void drawLine(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, oledColor color){
    int dx = abs(x2 - x1);
    int dy = abs(y2 - y1);
    int sx = (x1 < x2) ? 1 : -1;
    int sy = (y1 < y2) ? 1 : -1;
    int err = dx - dy;
    while(1){
        writePoint(x1,y1,color);
        if ((x1 == x2) && (y1 == y2)) break;
        int e2 = 2 * err;
        if (e2 > -dy) {
            err = err - dy;
            x1 = x1 + sx;
        }
        if (e2 <  dx) {
            err = err + dx;
            y1 = y1 + sy;
        }
    }
    printf("Draw Line Finish\n");
    //oledUpdate();
}

/**
 * @brief  画点
 * @param  x: 列
 * @param  y: 行
 * @param  color: 颜色
 */
void drawPoint(uint8_t x, uint8_t y, oledColor color){
    writePoint(x,y,color);
    writePoint(x+1,y,color);
    writePoint(x,y+1,color);
    writePoint(x+1,y+1,color);
    printf("Draw Point Finish\n");
    //oledUpdate();
}

/**
 *  @brief  在指定位置写入一个字符
 *  @param  x: column地址
 *  @param  y: page地址
 *  @param  ch: 字符
 */
void drawCharByPage(uint8_t x, uint8_t y, char ch){
    if(ch<32||ch>122){
        ch=' ';
    }
    if(x>127&&y>7){
        return;
    }
    setPos(x,y,x+1,y+1);
    for(int i=0;i<6;i++){
        oledWriteData(ascii8x6[ch-32][i]);
    }
}

/**
 *  @brief  在指定位置写入一个字符
 *  @param  x: 像素列地址
 *  @param  y: 像素行地址
 *  @param  ch: 字符
 */
void drawCharBypixel(uint8_t x, uint8_t y ,char ch){
    if(ch<32||ch>122){
        printf("Illegal character\n");
        ch=' ';
    }
    if(x>=OLED_WIDTH||y>=OLED_HEIGHT||
        x+ASCII_WIDTH>=OLED_WIDTH||y+ASCII_HEIGHT>=OLED_HEIGHT){
        printf("out of edge\n");
        return;
    }
    for(int i=0;i<ASCII_WIDTH;i++){
        uint8_t mask=0x01;
        uint8_t page=ascii8x6[ch-32][i];
        for(int j=0;j<ASCII_HEIGHT;j++){
            uint8_t color=(page&mask)?1:0;
            mask<<=1;
            writePoint(i+x,j+y,color);
        }
    }
    printf("Draw Character Finish!\n");
    //oledUpdate();
}

/**
 *  @brief  在指定位置写入汉字句子
 *  @param  x: 像素列地址
 *  @param  y: 像素行地址
 *  @param  row: 汉字行数
 *  @param  cnFonts: 汉字字模
 */
void drawChinese(uint8_t x, uint8_t y, size_t row, const uint8_t cnFonts[][CN_WIDTH]){
    int px=x;
    int py=y;
    size_t num_rows = row;
    for(size_t k=0;k<num_rows;k++){
        uint8_t com[CN_WIDTH];
        memcpy(com,cnFonts[k],sizeof(com));
        for(int i=0;i<CN_WIDTH;i++){
        uint8_t mask=0x01;
        uint8_t page=com[i];
        for(int j=0;j<8;j++){
            uint8_t color=(page&mask)?1:0;
            mask<<=1;
            writePoint(i+px,j+py,color);
            }
        }
        if(k%2==0){
            py+=8;
        }else{
            px+=16;
            py=y;
        }
    printf("Draw Character Finish!\n");
    
    }
    oledUpdate();
}

/**
 *  @brief  在指定位置写入字符串
 *  @param  x: 像素列地址
 *  @param  y: 像素行地址
 *  @param  str: 字符串
 */
void drawString(uint8_t x, uint8_t y, char* str){
    while(*str){
        drawCharBypixel(x,y,*str);
        x+=ASCII_WIDTH;
        str++;
    }
    printf("Draw Sting Finish!\n");
}